My focus has pretty much stayed within the vanilla realm, but I've been wanting to try out some JS frameworks to get a little bit of a feel for them.
I did try Ember during Code School's free weekend. Unfortunately, I did come across that news a little late so I wasn't able to get too far, but I did find it interesting. My plan is to go test it out on brief little app and document as I go as I did with Angular.
Below is my documentation as I went through different steps... I see why people like it, but I also see I barely scratched the surface of what Angular is capable of.
npm install @angular/cli -g
I have a vagrant setup so I ran above code within vagrant folder using command line from within environment.
ng -v
will check for Angular, if present Angular CLI welcome message will appear in terminal. Using terminal mentioned above it shows a successful install, however if that same command were run outside of that terminal ex: /vagrant-js folder you'll get 'command not found'
At this point, make sure you're in correct folder to create project folder. In my case, it's the src
folder. Once there, ng new projectnamehere
will create project.
The above will run for a bit, once done run ng serve
which reminds me of rails s
Initially ran ng new projectnamehere --style=scss --routing
, but ended up with a few npm errors and project not successfully being created. Removed --routing
recreated project using ng new projectnamehere --style=scss
was successful or so it seems.
Once I addressed project creation, I expected ng serve
to serve a welcome message that would make my heart smile... it did not (boooooooo). Looking up this issue, I've found few posts if any-- whatever found is similar, but not exact. From what I gather it seems angular and vagrant do not play nice with each other.
ng new projectnamehere --style=scss --routing
which was giving me issue within vagrant env, seems to be doing fine so far(status bar is 3/4 done).Project app successfully created and now for the moment of truth... drumroll localhost shows my angular welcome page. Hmmm...
I think the assumption/expectation would be happiness because it finally works and I'm able to move forward to play around with it, but... I'm kinda not. Why? Well, because I want to be able to set it up within my vagrant env as I have my other projects. I feel that although the vibe I'm getting from surfing the interwebzzz is that it is not possible... a part of me feels I can find a way. Which I will get back on that quest after... because... I'm totally going to play with it right now! :D
scripts ending with .ts
stands for typescript
to create components
ng generate component nameofcomponenthere
or shorthand ng g c nameofcomponenthere
above code reminds me of rails g controller nameofcontroller
I'm going to have to double check though as I haven't been playing with rails since focusing on frontend and all it entails.
app component
seems to be the base-- this is where you are able to nest additional components such as about
or home
which I created in this particular project.
routerLink
will allow you to change actual component
<a routerLink="about"> ABOUT </a>
creates ABOUT link
when looking into components created (ex: home & about) their html pages come with
<p>about works!</p>
I like that it comes with some test text.
In this case, when testing out the nesting of components I was able to see the test text show up under the home & about links created letting me know the nesting was successful. Woo!
templates and style paths are found in componentnamehere.components.ts
you are able to write inline code in these areas, but personally it just seems messy to do so even if it is just one line or two. I like the fact things are separated.
With that said, there is a styles.scss
file where you are able to put global styling while more specific styles go in respective components scss file.
what makes things functional/ interactive^
interpolation example:
in home.component.ts
putting property itemCount: number = 4
-- by going to home.component.html
and placing ({{ itemCount }})
will show the hardcoded itemCount of 4.
I see similarities in the way itemCount
is used to pull hardcoded number from typescript file, perhaps it was using Jekyll, liquid or maybe it was when playing with Ember.
property binding example:
btnText: string = 'add task';
in home.component.ts
<input type="submit" class="btn" [value]="btnText">
in home.component.html
vs. initially, <input type="submit" class="btn" value="add">
or interpolation <input type="submit" class="btn" value="{{ btnText }}">
both property and interpolation example are one-way binding
2-way data binding example:
In the case of the todo input, setting and retrieval of value/data from component class would be necessary. ng model
would create that 2-way data binding. For this to work there needs to be an import of module.
add import { FormsModule } from '@angular/forms';
into app.module.ts
as well as under imports
section.
as mentioned before app files are the basis of the project.
to test this out-- hardcoded item is added in home.component.ts
under previous examples goalText: string = 'todo item example';
adding <input type="text" class="txt" name="item" placeholder="ToDo..." [(ngModel)]="goalText"><br><span>{{ goalText }}</span>
in form within html file-- comes from component to html template (1 direction atm)
editing text u can see the reverse direction coming from the component class being set by value in input field (2-way data binding)-- capturing user input and communicate to class however, you cant actually add anything yet/ save user submitted data somewhere-- we need event binding
event binding: captures user initiated events to initiate logic in component class there are diff types of events, in this example click event is used
<input type="submit" class="btn" [value]="btnText" (click)="addItem()">
adding click event, uses parenthesis with method call, arg not needed since input using ngModel
saved in component class.
in home.component.ts
ngOnInit()
gets loaded when app loads
itemCount: number;
btnText: string = 'add task';
goalText: string = 'todo item example';
goals = [];
constructor() { }
ngOnInit() {
this.itemCount = this.goals.length;
}
addItem() {
this.goals.push(this.goalText);
this.goalText = '';
this.itemCount = this.goals.length;
}
the above shows count of items added, dynamically
however, the actual items do not show yet because we have not edited html file to do so.
<div class="col">
<p class="todo-container" *ngFor="let goal of goals">
{{ goal }}
</p>
</div>
^above shows items added, matches dynamic count
the ngFor
, a for loop? using interpolation {{ goal }}
to iterate through? from pervious goals
array placed in home.component.ts
adding animation capabilities npm install @angular/animations@latest --save
when restarting server, there were some errors-- it turns out I was missing a comma where imports are listed
what's cool is once I fixed the typo the terminal updated without me having to quit/restart the server/console
it actually found another error in spelling and then once I fixed it it updated again... too cool.
after the npm install, we need accessibility so as with FormsModule
we add import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
to app.module.ts
script and then again under imports
section.
in home.component.ts
we then import specific types of animation, such as trigger.
import { trigger, style, transition, animate, keyframes, query, stagger } from '@angular/animations'
to use them you place animation specific functions under @Component
section
trigger('')
first arg is name of animation in this case it will be called goals
trigger('goals')
then an array for animation specific functions
trigger('goals', [
])
after defining animation, add to template home.component.html
as follows--
<div class="container color-light" [@goals]="goals.length">
the @goals
is the name of animation
NOTE: alot of the animation seems like overkill, good to know as overview
in home.component.html
--
<div class="col">
<p class="todo-container" *ngFor="let goal of goals; let i = index" (click)="removeItem(i)">
{{ goal }}
</p>
</div>
in home.component.ts
--
export class HomeComponent implements OnInit {
itemCount: number;
btnText: string = 'add task';
goalText: string = 'todo item example';
goals = ['test', 'test2', 'test3'];
constructor() { }
ngOnInit() {
this.itemCount = this.goals.length;
}
addItem() {
this.goals.push(this.goalText);
this.goalText = '';
this.itemCount = this.goals.length;
}
removeItem(i) {
this.goals.splice(i,1);
}
}
when creating project ng new projectnamehere --style=scss --routing
--routing
created app-routing.module.ts
there we import both components created ex: home & about
import { HomeComponent } from './home/home.component'
import { AboutComponent } from './about/about.component'
where we set paths--
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component'
import { AboutComponent } from './about/about.component'
const routes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'about',
component: AboutComponent
}
];
upon saving and browser refreshing there will be 2 of everything created initially, this is because of the app.component.html
file where the following line is located <app-home></app-home>
, removing it will revert to displaying 1 of everything as it was before adding the routing.
clicking on menu links, home & about, are now functional
:id
is route parameter
example:
{
path: 'about/:id',
component: AboutComponent
}
for example hardcoding an :id parameter--
<li><a routerLink="about/32">ABOUT</a></li>
when clicking on about link it will show hardcoded id parameter in address bar... but how do we retrieve that?
by adding import { ActivatedRoute } from '@angular/router';
in about.component
it will give access to route parameters and creating an instance of ActivatedRoute through dependency injection
constructor(private route: ActivatedRoute) {
this.route.params.subscribe(res => console.log(res.id));
}
^above fetches params showing in console.
Normally there would be a property binded to constructor value and do something with data such as query an api to get data from some db
changing router component based on logic occurring in component class
ex: import { Router } from '@angular/router'
in about.component
and make an instance as done with ActivatedRoute
constructor(private route: ActivatedRoute, private router: Router) {
this.route.params.subscribe(res => console.log(res.id));
}
add a method
sendMeHome() {
this.router.navigate([''])
}
the reason the brackets within parenthesis is empty is due to the empty string in home path in app-routing.module.ts
<p>
about works! <a href="" (click)="sendMeHome()"><strong>now, send me back.</strong></a>
</p>
This will send you back homepage using above method from about.component.ts
in order to access code used in various parts w/o rewriting-- service files are used to share data between components
ng generate service data
to create service file to share data btwn home and about components
one of the best ways to share data between components is to use import { BehaviorSubject } from 'rxjs/BehaviorSubject';
Below is what it would look like--
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class DataService {
private goals = new BehaviorSubject<any>(['todo 1', 'todo 2']);
goal = this.goals.asObservable();
constructor() { }
changeGoal(goal) {
this.goals.next(goal);
}
}
to use service file, it needs to be imported into app.module.ts
adding import { DataService } from './data.service'
and in provider array providers: [DataService],
import { DataService } from '../data.service';
also needs to be added to home.component.ts
putting two dots at beginning of path since it is within a folder.
needs an instance, which is created through dependency injection... in constructor constructor(private _data: DataService) { }
why the underscore?^
then...
ngOnInit() {
this.itemCount = this.goals.length;
this._data.goal.subscribe(res => this.goals = res);
this._data.changeGoal(this.goals);
}
I'm still annoyed at the fact there seems to be no way to run Angular within Vagrant.
Also, since I have git setup within Vagrant and not on my machine I didn't push up my play test... booooooo!